<?php
/*
 *
 * @package MoltX
 * @author $Author$
 * @version $Id$
 * @link $URL$
 * @copyright Copyright (c) 2005-2010, Matthew Oltendorf
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 *
 */

blink_console :: setup_console ( );

/*
 * Console class
 * @package Blink
 *
 * Singleton
 */
final class blink_console
{
	private static	$pid_current = null,
					$pid_file = null,
					$stream_file = null,
					$stream_resource = null;
	/*
	 * Constructor function
	 *
	 * This is private to enforce singleton mode.
	 *
	 * @return null
	 */
	private function __construct ( )
	{
		return null;
	}

	/*
	 * Active check function
	 *
	 * Checks if there's any other console log servers that are active.
	 *
	 * @return boolean
	 */
	private static function active ( )
	{
		try
		{
			$pid = file_get_contents ( self :: $pid_file );

			if ( $pid == self :: $pid_current )
			{
				return true;
			}
		}
		catch ( Exception $exception )
		{
			// Ignore.
		}

		return false;
	}

	/*
	 * Log message function
	 *
	 * @param string $input the message to log.
	 *
	 * @return boolean
	 */
	public static function log ( $input )
	{
		// Create the message to be sent to the log server.
		$room = is_string ( blink :: $room ) ? ' ' . blink :: $room . ' ' : '';
		$message = blink :: $role . $room . ' [' . self :: $pid_current . ']: ' . $input;

		try
		{
			$socket = stream_socket_client ( 'unix://' . self :: $stream_file );
			fwrite ( $socket, $message );
			fclose ( $socket );
		}
		catch ( Exception $exception )
		{
			/*
			 * For now we'll throw another exception. Logging shouldn't be
			 * included in the production releases of the program.
			 */

			throw new Exception ( 'COULD NOT WRITE TO LOG' );

			return false;
		}

		return true;
	}

	/*
	 * Listen loop function
	 *
	 * Listens for incoming connections and logs them to the screen.
	 *
	 * @return null
	 */
	private static function loop ( )
	{
		for ( ;; )
		{
			try
			{
				// @todo: Change to a higher interval for production.
				$client = stream_socket_accept ( self :: $stream_resource, 1 );

				$input = stream_get_contents ( $client );
				fclose ( $client );

				echo date ( 'H:i:s ' ), $input, "\n";
			}
			catch ( Exception $exception )
			{
				if ( ! self :: active ( ) )
				{
					break;
				}

				continue;
			}
		}

		return null;
	}

	/*
	 * Serve function
	 *
	 * Serves as a log server.
	 *
	 * @return null
	 */
	public static function serve ( )
	{
		// Create a file with the script's process identifier inside.
		$pointer = fopen ( self :: $pid_file, 'w+' );
		blink :: lock ( $pointer );
		fwrite ( $pointer, self :: $pid_current );
		blink :: lock ( $pointer, false );
		fclose ( $pointer );

		if ( file_exists ( self :: $stream_file ) )
		{
			// We have to delete this file for the script to be able to listen.
			unlink ( self :: $stream_file );

			sleep ( 1 );
		}

		$stream = stream_socket_server ( 'unix://' . self :: $stream_file );

		if ( ! $stream )
		{
			// Error.
			throw new Exception ( 'COULD NOT OPEN STREAM SERVER' );
		}

		self :: $stream_resource = $stream;

		// Clean up variables as we could be here a very, very long time...
		unset ( $pointer, $stream );

		if ( blink_browser )
		{
			// Set headers.
			header ( 'Content-Type: text/plain' );
		}

		// Always flush after echo.
		ob_implicit_flush ( true );

		echo date ( 'H:i:s ' ), 'console [', self :: $pid_current, ']: role spawned', "\n";
		echo date ( 'H:i:s ' ), 'console [', self :: $pid_current, ']: listening for connections', "\n";

		// Start the listen loop.
		self :: loop ( );

		echo date ( 'H:i:s ' ), 'console [', self :: $pid_current, ']: role closed', "\n";

		fclose ( self :: $stream_resource );

		return null;
	}

	/*
	 * Constructor function
	 *
	 * Set up the console environment.
	 *
	 * @return null
	 */
	public static function setup_console ( )
	{
		self :: $pid_file = blink_storage . '/socket/console.pid';

		if ( file_exists ( self :: $pid_file ) )
		{
			$pid = file_get_contents ( self :: $pid_file );

			if ( $pid > 98 )
			{
				$pid = 0;
			}
			else
			{
				++$pid;
			}
		}
		else
		{
			$pid = 0;
		}

		self :: $pid_current = $pid;
		self :: $stream_file = blink_storage . '/socket/console.sock';

		return null;
	}
}
